<template>
  <div :class="['upload-file', size, limit === 1 ? 'single' : '']">
    <el-upload
      ref="uploadRef"
      :class="['upload-file-uploader', overLimit ? 'hide-upload-btn' : '']"
      :multiple="limit !== 1"
      :action="uploadImgUrl"
      :data="extraData"
      :headers="headers"
      :limit="limit"
      :disabled="disabled"
      :file-list="fileList"
      list-type="picture-card"
      :show-file-list="true"
      :before-upload="handleBeforeUpload"
      :on-success="handleUploadSuccess"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-change="handleChange"
      :on-remove="handleRemove"
      :on-preview="previewShow">
      <el-icon class="avatar-uploader-icon"><plus /></el-icon>

      <!-- 上传提示 -->
      <template #tip>
        <div class="el-upload__tip" v-if="showTip">
          <template v-if="tips"> {{ tips }}</template>
          <template v-else>
            请上传
            <template v-if="min > 0 && limit > 0">
              <template v-if="min === limit"
                ><b style="color: #f56c6c">{{ limit }}</b
                >张</template
              >
              <template v-else
                ><b style="color: #f56c6c">{{ min }}-{{ limit }}</b
                >张</template
              >
            </template>
            <template v-else-if="min > 0">
              至少<b style="color: #f56c6c">{{ min }}</b
              >张</template
            >
            <template v-else-if="limit > 0">
              最多<b style="color: #f56c6c">{{ limit }}</b
              >张</template
            >
            <template v-if="fileSize">
              大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
            </template>
            <template v-if="fileTypeList">
              格式为 <b style="color: #f56c6c">{{ fileTypeList.join('/') }}</b>
            </template>
            的图片
          </template>
        </div>
      </template>
    </el-upload>

    <image-preview init-show :src="dialogImageUrl" @close="previewClose" v-if="dialogVisible" />
  </div>
</template>

<script setup name="ImageUpload">
  import { getToken } from '@/utils/user'
  import { RESPONSE, UPLOADER } from '@/config'
  import { getFileExt, getFileName } from '@/utils/tools/url'
  import { staticUrl } from '@/utils/frame'

  const props = defineProps({
    defaultList: [String, Array], // 默认列表
    resultArray: Boolean, // value的值是否是数组，true返回数组{name:xxx,path:xxx}，false返回字符串
    disabled: Boolean, // 是否禁用状态
    size: String, // 展示大小 small、mini 或 空
    button: String, // 上传按钮名称
    overHideUpload: Boolean, // 上传超过数量是否隐藏上传按钮
    extraData: Object, // 上传额外操作
    min: Number, // 最少数量
    // 图片数量限制
    limit: {
      type: Number,
      default: 5
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 5
    },
    // 图片类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => []
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true
    },
    // 提示语，为空自动生成
    tips: String
  })
  const uploading = defineModel('loading', { default: false }) // 是否上传中
  const modelList = defineModel() // 同步值

  const { proxy } = getCurrentInstance()
  const uploadRef = useTemplateRef('uploadRef')
  const uploadList = ref([]) // 上传的列表
  const fileList = ref([]) // 出示给element组件的例表
  const headers = ref({ Authorization: 'Bearer ' + getToken() })

  const uploadImgUrl = ref(UPLOADER.IMAGE) // 上传的图片服务器地址
  const emit = defineEmits()

  const dialogImageUrl = ref('')
  const dialogVisible = ref(false)

  watch(
    () => props.defaultList,
    (val, _) => {
      if (val) {
        // 首先将值转为数组
        const list = Array.isArray(val) ? val : val.split(',')
        // 然后将数组转为对象数组
        let temp = 1
        fileList.value = list.map(item => {
          if (typeof item === 'string') item = { name: getFileName(item), path: item }
          item.url = staticUrl(item.path)
          item.uid = item.uid || new Date().getTime() + temp++
          return item
        })
      } else {
        fileList.value = []
      }
      uploadList.value = fileList.value
    },
    { deep: true, immediate: true }
  )

  // 计算是否显示提示
  const showTip = computed(() => props.isShowTip && (props.tips || props.fileType || props.fileSize))
  // 计算是否超过限制
  const overLimit = computed(() => {
    if (props.disabled) return true
    if (props.limit < 1 || !props.overHideUpload) return false
    return uploadList.value.length >= props.limit
  })
  // 计算获取上传图片类型后缀限制
  const fileTypeList = computed(() => {
    if (props.fileType && props.fileType.length) {
      if (props.fileType[0] === '*') return ['jpg', 'jpeg', 'png', 'gif']
      return props.fileType
    }
    return ['jpg', 'jpeg', 'png', 'gif']
  })

  // 图片个数超出
  function handleExceed() {
    proxy.$modal.msgError(`上传图片数量不能超过 ${props.limit} 张!`)
  }

  // 上传失败
  function handleUploadError(_, file) {
    proxy.$modal.msgError((file?.name || '图片') + ' 上传失败')
  }

  // 上传前校检格式和大小
  function handleBeforeUpload(file) {
    if (props.disabled) {
      proxy.$modal.msgError('当前不支持上传操作!')
      return false
    }

    // 校检文件类型
    if (fileTypeList.value.length) {
      const fileExt = getFileExt(file.name, true)
      if (!fileTypeList.value.includes(fileExt)) {
        proxy.$modal.msgError(`图片格式不正确，请上传${props.fileType.join('/')}格式文件!`)
        return false
      }
    } else if (file.type.indexOf('image') === -1) {
      proxy.$modal.msgError(`请上传图片格式图片!`)
      return false
    }
    // 校检文件名是否包含特殊字符
    if (file.name.includes(',')) {
      proxy.$modal.msgError('图片名不正确，不能包含英文逗号!')
      return false
    }
    if (props.fileSize) {
      const isLt = file.size / 1024 / 1024 < props.fileSize
      if (!isLt) {
        proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`)
        return false
      }
    }
    return true
  }

  // 监听 文件新增、成功、失败
  function handleChange(_, files) {
    uploading.value = files.some(item => {
      return item.status !== 'success' && item.status !== 'fail'
    })
    uploadList.value = files
    let okList = []
    files.map(item => {
      if (item.status === 'success') okList.push({ name: item.name, path: item.path })
    })

    if (props.resultArray) {
      modelList.value = okList
    } else {
      modelList.value = listToString(okList, ',')
    }
  }

  // 监听文件删除
  function handleRemove(file, fileList) {
    handleChange(file, fileList)
  }

  // 自定义删除文件
  function removeClick(file) {
    uploadRef.value.handleRemove(file)
  }

  // 上传成功回调
  function handleUploadSuccess(res, file) {
    if (res.code === RESPONSE.SUCCESS_CODE) {
      file.url = res.data.url
      file.path = res.data.path
    } else {
      proxy.$modal.msgError(file.name + (res?.msg || '上传失败'))
      removeClick(file)
    }
  }

  // 预览
  function previewShow(file) {
    dialogImageUrl.value = file.url
    dialogVisible.value = true
  }

  function previewClose() {
    dialogImageUrl.value = ''
    dialogVisible.value = false
  }

  // 对象转成指定字符串分隔
  function listToString(list, separator) {
    let result = ''
    separator = separator || ','
    list.map(item => {
      if (item.path) result += item.path + separator
    })
    return result ? result.slice(result, result.length - 1) : ''
  }
</script>

<style scoped lang="scss">
  .upload-file {
    line-height: 1;
  }
  .hide-upload-btn {
    :deep(.el-upload) {
      display: none;
    }
  }
  .el-upload__tip {
    line-height: 1.3;
    color: #888;
  }

  .single {
    :deep(.el-upload-list--picture-card .el-upload-list__item) {
      margin: 0;
    }
  }

  .small {
    :deep(.el-upload.el-upload--picture-card),
    :deep(.el-upload-list--picture-card .el-upload-list__item) {
      width: 88px;
      height: 88px;
    }
    :deep(.el-upload.el-upload--picture-card) {
      line-height: 88px;
    }
  }
  .mini {
    :deep(.el-upload.el-upload--picture-card),
    :deep(.el-upload-list--picture-card .el-upload-list__item) {
      width: 60px;
      height: 60px;
    }
    :deep(.el-upload.el-upload--picture-card) {
      line-height: 60px;
    }
    :deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
      font-size: 16px;
    }
    :deep(.el-upload--picture-card > i) {
      font-size: 24px;
    }
  }

  .table {
    :deep(.el-upload.el-upload--picture-card),
    :deep(.el-upload-list--picture-card .el-upload-list__item) {
      width: 40px;
      height: 40px;
    }
    :deep(.el-upload.el-upload--picture-card) {
      line-height: 40px;
    }
    :deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
      font-size: 14px;
    }
    :deep(.el-upload--picture-card > i) {
      font-size: 18px;
    }
    :deep(.el-upload-list__item-status-label) {
      height: 24px;
      right: -16px;
      top: -8px;
      transform: rotate(45deg) scale(0.6);
      width: 40px;
    }
  }
  :deep(.el-upload-list__item .el-icon--close-tip) {
    opacity: 0;
  }

  :deep(.el-upload-list--picture-card .el-upload-list__item-actions span + span) {
    margin-left: 0;
  }

  :deep(.el-upload-list--picture-card .el-upload-list__item-actions) {
    justify-content: space-around;
  }

  // 去掉动画效果
  :deep(.el-list-enter-active),
  :deep(.el-list-leave-active) {
    transition: all 0s;
  }

  :deep(.el-list-enter),
  :deep(.el-list-leave-active) {
    opacity: 0;
    transform: translateY(0);
  }
</style>
